
//抽象类
//通常情况下【父类表示约束统一特征】，没有实际意义
//限制公有类[父类]做实例化对象操作：抽象类
//关键字：abstract  写在class之前
//抽象类不能实例化对象！
//抽象类可以属性，可以写普通方法！ 也可以写抽象方法[用abstract修饰，方法没有方法体]
//抽象方法：没有方法体的方法，只能写在抽象类中，使用关键字abstract
//子类一旦继承抽象父类，就必须重写父类的抽象方法
//抽象类还是类，类里面还是会包含：构造函数、属性、方法
//             描述特征，和普通类无差别

//抽象父类
abstract class Pet{
    // public name:string;
    // public show():void{
    //     console.log("业务逻辑")
    // }
    public abstract test():void;

}

// class Animal{
//     public name:string;
//     public show():void{
//         console.log("业务逻辑")
//     }
//     //public abstract test():void;
// }

class Dog extends Pet{

    public test(): void {
        //逻辑代码
        console.log("dog子类方法")
    }

}

class Cat extends Pet{

    public test(): void {
        console.log("cat子类方法")
    }
    
}

//var myPet = new Pet();//父类不能实例化了

var dog = new Dog();
//dog.test();
var cat = new Cat();

var petArr = [dog,cat];
petArr[0].test();
petArr[1].test();


//父类是一种约束, 约束所有子类具有的共同的特征

//接口是一种约束，约束个别实例对象还能够扩展具有的其他特征
//关键字：interface
//接口可以包含的内容：
//   属性:
//        不能显示的写修饰符，默认都是public的
//        接口中的属性不能显示的赋值     
//   方法：
//        方法都是没有方法体的。可以理解为抽象方法，只是不需要写abstract
//        不能显示的写修饰符，默认都是public的
//   构造函数不能写
//使用接口：
//    实现类 implements 接口
//注意：
//    接口中没有构造函数，不能实例化对象
//    接口的使用是通过实现类来操作的
//    实现类一旦实现接口，就必须重写接口的方法，以及添加接口中描述的属性
//    实现类同时拥有父类和接口的特征，继承extends在前，实现implements在后

//    一个实现类实现多个接口，一个子类只能有一个父类。
//接口
interface JK1{

    //constructor():void; //会被识别为普通方法
    //属性
    shuxing:string;
    //方法
    gongneng1():void;

}

class Lei implements JK1{

    //自己特有的特征
    shuxing: string;
    gongneng1(): void {
       console.log("方法体")
    }

}

//抽象父类：描述特征的
abstract class FuLei{
    public name:string;
    public abstract show():void;
}
//接口：提供能力的
interface JieKou{
    test():void;
    test1():void;
}

//接口：提供能力的
interface JieKou2{
    age:number;
    test2():void;
}

class MyTestClass extends FuLei implements JieKou,JieKou2{

    age: number;
    
    //父类约束必须具有的公共特征
    public show(): void {
        console.log("父类约束的show方法")
    }

    //接口约束必须具备的特有特征
    test(): void {
        console.log("接口约束的test方法")
    }
    test1(): void {
        console.log("接口约束的test1方法")
    }

    test2(): void {
        console.log("另一个接口约束的test2方法")
    }

}
//var jk = new JK1();不能实例化

var mtc = new MyTestClass();
mtc.show();
mtc.test();
mtc.test1();
mtc.test2();
mtc.name= "";
mtc.age = 1;

//使用面向对象实现租车系统：
//系统提供小轿车和大卡车两个车型的出租。
//小轿车按照：座位数、品牌来决定日租金。 座位数*100 + 500  / 天
//大卡车按照：吨位数、品牌来决定日租金。 吨位数*1000 + 1000 / 天


//接口本身也是类型，也可以做为类型使用
interface GNJK{
    test();
}

let bl:GNJK;
bl = {
    test(){
        console.log("实现接口的方法")
    }
};
console.log(bl);

//接口的扩展：支持同名接口，会将所有同名接口里的特征全部交给实现类
// 累加操作
interface jiekou{
    num:number;
}

interface jiekou{
    str:string;
    show():void;
}

var info:jiekou;
info = {
    num:10,
    str:"abc",
    show(){
        console.log('111');  
    } 
};
class Teacher implements jiekou{
    num: number = 1;
    str: string = "";
    show(): void {
        console.log('111');
    }
}

//接口对比类型别名
type mytype = {
    name:string;
}
interface myinter{
    name:string;
}
//相同点
// var bl1:mytype;//自定义类型别名
// bl1 = {
//     name:""
// }
// var bl2:myinter;//接口
// bl2 = {
//    name:"" 
// }

//不同点
//接口支持同名累加，类型别名不支持同名累加
interface jk{//同名接口
    num:number;
}
interface jk{//同名接口
    str:string;
    show():void;
}
let shuju:jk;
shuju={ // 对象包含所有同名接口特征
    num:1,
    str:"",
    show(){
    }
}
//类型别名不能重名的
// type lxbm ={//重名报错
//     name:string;
// }
// type lxbm ={//重名报错
//     age:number;
// }

//类型别名的扩展使用 &
type kzType = mytype & {
    age:number;
}
let sj:kzType;
sj = {
    name:"",
    age:1
}


//非泛型：
//在定义时就定下类型
// function fun(num:number){
// }
// fun(123)

//泛型：约束类型，当运行时才定下类型
//语法：
//    实际使用时写 <类型>  来代替 编码时写 <T>
//在函数名之后写<T>来说明 后续参数和返回值是何类型
function fn<T>(num:T):T{
    return num;
}
let result = fn<number>(123);
console.log(typeof(result));

let result2 = fn<string>("str");
console.log(typeof(result2));

//文字类型
//'Hello'文字类型
let ctx:'Hello';
ctx = "Hello";
let xbl:true;
xbl = true;
//对比常量
const cl = "abc";
//cl = "";

//参数是文字类型
function printText(str:string, option:'left'|'right'|'center'){
}
printText('abc','left');

//对象中取字符串，并不是文字类型
function printText2(option:'left'|'right'|'center'){//类型
}

//定义类型别名
type tt = {
    str2:'left' //文字类型
}
//使用自定义类型
let le:tt;
le = {//le对象的属性str2用的类型就是文字类型
    str2: 'left'//文字类型的left值
}
var obj={//对象obj的属性str2用的类型默认是字符串
    str1:'abc',
    str2:'left', //字符串类型的值
    str3:'left' as 'left' // 将字符串left值 断言成 left文字类型
} 
//参数写le.str2 正确   文字类型
//参数写obj.str2 错误  字符串类型
//参数写obj.str3 正确  字符串转文字类型
printText2(obj.str3); //obj.str2





